#include <iostream>
#include <utility>
#include "coroutine.h"

class Fibonacci : public AbsRoutine<int> {
public:
    explicit Fibonacci(int n) : n(n) {}

    int resume() override {
        CO_BEGIN();
                for (i = 0; i < n; i++) {
                    c = a + b;
                    YIELD(c);
                    b = a;
                    a = c;
                }
        CO_END(-1);
    }

private:
    int a = 1, b = 0, c = 0, i = 0;
    int n{};
};

void test_fib() {
    printf("Test Fibonacci in Coroutine (Switch):\n");
    Fibonacci f{10};
    for (auto res = f.resume(); f.status() != DEAD; res = f.resume()) {
        printf("%d\t", res);
    }
    printf("\n\n");
}

class Show : public AbsRoutine<void> {
public:
    explicit Show(std::string txt) : txt(std::move(txt)) {}

    void resume() override {
        CO_BEGIN();
                for (i = 0; i < 4; ++i) {
                    std::cout << i << " : " << txt << std::endl;
                    YIELD();
                }
        CO_END();
    }

private:
    int i = 0;
    std::string txt{};
};

void hello_world_switch() {
    printf("Test Hello World in Coroutine (Switch):\n");
    Show task[2] = {Show("Hello,"), Show("World!")};
    bool flag = true;
    while (flag) {
        flag = false;
        for (auto &i: task) {
            if (i.status() != DEAD)
                i.resume();
            flag |= (i.status() != DEAD);
        }
    }
    printf("\n");
}

void hello() {
    for (int i = 0; i < 4; ++i) {
        printf("%d : Hello,\n", i);
        co_asm::yield();
    }
    co_asm::end();
}

void world() {
    for (int i = 0; i < 5; ++i) {
        printf("%d : World!\n", i);
        co_asm::yield();
    }
    co_asm::end();
}

void hello_world_asm() {
    printf("Test Hello World in Coroutine (ASM):\n");
    std::vector<co_asm::AsmRoutine> task = {co_asm::AsmRoutine(hello), co_asm::AsmRoutine(world)};
    co_asm::event_loop(task, 1);
}

int main() {
    test_fib();
    hello_world_switch();
    hello_world_asm();
}